#pragma once
#include "log.hpp"
#include <iostream>
#include <unordered_map>
#include <cstdio>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <queue>

#define SIZE 1024 //定义缓冲区的大小

//封装UdpServer类
class UdpServer
{
public:
    UdpServer(uint16_t port, std::string ip = "")
             : _port(port), _ip(ip), _sock(-1)
            {}

    
    bool initServer()
    {
        // 从这里开始，就是新的系统调用，来完成网络功能啦
        //  1. 创建套接字
        _sock = socket(AF_INET, SOCK_DGRAM, 0); 
        // AF_INET==FP_INET，socket创建成功本质上会返回文件描述符
        // 第一个参数用来说明是本地通信还是网络通信
        // 第二个参数用来确定是以字节流的方式还是数据报的方式
        // 第三个参数忽略填0 
        // 如果创建失败，输出失败信息
        if (_sock < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        // 2. bind: 将用户设置的ip和port在内核中和我们当前的进程强关联
        // int bind(int socket, const struct sockaddr *address, socklen_t address_len);
        struct sockaddr_in local;        //sockaddr_in的头文件 #include <netinet/in.h> #include <arpa/inet.h>
        bzero(&local, sizeof(local));    //将数据进行清零
        local.sin_family = AF_INET;      //表示网络通信
        // 服务器的IP和端口未来也是要发送给对方主机的 -> 先要将数据发送到网络！ 所以进行主机序列转换为网络序列
        local.sin_port = htons(_port);   //主机端口号转为网络序列化数据
        // "192.168.110.132" -> 点分十进制字符串风格的IP地址，每一个区域取值范围是[0-255]: 1字节 -> 4个区域
        // 理论上，表示一个IP地址，其实4字节就够了！！ 所以要将点分十进制字符串风格的IP地址 -> 4字节ip地址
        // 1. 先要将点分十进制字符串风格的IP地址 ->  4字节
        // 2. 4字节主机序列 -> 网络序列
        // 有一个接口inet_addr，可以一次帮我们做完这两件事情
        // INADDR_ANY这个宏就0，让服务器在工作过程中，可以从任意IP获取数据
        local.sin_addr.s_addr = _ip.empty() ? INADDR_ANY : inet_addr(_ip.c_str());//让服务器在工作过程中，可以从任意IP中获取数据
        //bind
        //如果bind失败，输出错误信息
        if (bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            logMessage(FATAL, "%d:%s", errno, strerror(errno));
            exit(2);
        }
        //输出正常运行的信息
        logMessage(NORMAL, "init udp server done ... %s", strerror(errno));
        //初始化服务器成功
        return true;
    }

    //启动服务器
    void Start()
    {
        // 服务器的功能是， client给我们发送消息，暂时原封不动返回
        // 作为一款网络服务器，永远不退出的！
        // 服务器启动-> 进程 -> 常驻进程 -> 永远在内存中存在，除非挂了！
        char buffer[SIZE]; //定义缓冲区
        for (;;)
        {            
            struct sockaddr_in peer;// 注意：peer,纯输出型参数，表示对方的信息
            bzero(&peer, sizeof(peer)); //初始化结构体，将数据进行清零
            socklen_t len = sizeof(peer); //len是输入输出型参数，输入: peer缓冲区大小；输出: 实际读到的peer；固定写法
            char result[256];
            char key[64];               //记录客户端的ip和端口
            std::string cmd_echo;
            // start. 读取数据
            ssize_t s = recvfrom(_sock, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (s > 0)
            {
                buffer[s] = 0; // 目前数据当做字符串
                // 1. 输出发送的数据信息
                // 2. 输出是谁发送的信息
                uint16_t cli_port = ntohs(peer.sin_port);      // 从网络中来的，需要将网络序列转换为主机序列
                std::string cli_ip = inet_ntoa(peer.sin_addr); // 4字节的网络序列的IP->本主机的字符串风格的IP，方便显示
                snprintf(key, sizeof(key), "%s-%u", cli_ip.c_str(), cli_port); // 127.0.0.1-8080
                logMessage(NORMAL, "key: %s", key);
                //查找hash中是否存在该用户
                auto it = _users.find(key);
                if (it == _users.end())
                {
                    // exists
                    logMessage(NORMAL, "add new user : %s", key);
                    //添加用户对象
                    _users.insert({key, peer});
                }
            }
            // 分析和处理数据，TODO
            // end. 写回数据
            for (auto &iter : _users)
            {
                std::string sendMessage = key;
                sendMessage += "# ";
                sendMessage += buffer; // 127.0.0.1-1234# 你好
                //打印日志信息
                logMessage(NORMAL, "push message to %s", iter.first.c_str());
                //sendto发送数据
                sendto(_sock, sendMessage.c_str(), sendMessage.size(), 0, (struct sockaddr *)&(iter.second), sizeof(iter.second));
            }
        }
    }

    ~UdpServer()
    {
        if (_sock >= 0) 
            close(_sock);//关闭套接字
    }

private:
    // 一个服务器，一般必须需要ip地址和port(16位的整数)
    uint16_t _port;         //声明端口号
    std::string _ip;        //声明ip地址
    int _sock;              //声明通信所有的套接字
    std::unordered_map<std::string, struct sockaddr_in> _users; //声明用户的hash结构
    //std::queue<std::string> messageQueue;       //声明消息队列，读写解耦，生产消费者模型
};

//云服务器无法绑定公网ip，因为都是虚拟ip
//服务器无不推荐绑定特定ip的方法，推荐任意ip的方案
